<template>
  <form-component />
</template>

<script setup lang="tsx">
import { Col, Form, FormItem, Row, Space } from 'tdesign-vue-next';
import { computed, CreateComponentPublicInstance, onMounted, provide, ref } from 'vue';

import * as components from './models/index';
import {
  ComponentsKeysType,
  EmitsType,
  FormItemPropsType,
  FormRowType,
  PropsType,
  TDColType,
  TDFormItemType,
  TDFormType,
  TDRowType,
  TDSpaceType,
} from './type.d';

const { formAttribute = {}, formItems = [], spaceAttribute = {} } = defineProps<PropsType>();

const defaultFormAttribute: TDFormType = {
  colon: true,
  labelAlign: 'right',
  labelWidth: '150px',
};

const defaultRowAttribute: TDRowType = {
  gutter: [24, 24],
};
const defaultColAttribute: TDColType = {
  span: 12,
};
const defaultFormItemAttribute: TDFormItemType = {};
const defaultSpaceAttribute: TDSpaceType = {
  direction: 'vertical',
  size: 24,
  style: {
    width: '100%',
  },
};

const emits = defineEmits<EmitsType>();

const formData = defineModel<Record<string, any>>({});

provide('dynamicFormData', formData);

const createFormItem = (formItemProps: FormItemPropsType) => {
  const { colAttribute, formItemAttribute, component, name, componentAttribute } = formItemProps;

  const mergedFormItemAttribute = computed(() => ({
    ...defaultFormItemAttribute,
    ...formItemAttribute,
  }));

  const mergedComponentAttribute = computed(() => ({
    ...componentAttribute,
  }));

  let formItemComponent: { new (...args: any[]): CreateComponentPublicInstance<any> };
  if (typeof component === 'string') {
    const componentName = component as ComponentsKeysType;
    formItemComponent = components[componentName] || null;
  }

  return (
    <Col {...defaultColAttribute} {...colAttribute}>
      {formItemComponent ? (
        <formItemComponent
          formItemAttribute={mergedFormItemAttribute.value}
          componentAttribute={mergedComponentAttribute.value}
          name={name}
        />
      ) : (
        <FormItem {...mergedFormItemAttribute.value}>
          <component v-model={formData.value[name]} is={component} name={name} {...mergedComponentAttribute.value} />
        </FormItem>
      )}
    </Col>
  );
};

const FormComponent = () => (
  <Form ref={formDom} {...defaultFormAttribute} {...formAttribute} data={formData}>
    <Space {...defaultSpaceAttribute} {...spaceAttribute}>
      {formItems.map((item: FormRowType) => {
        const { rowAttribute = {}, children = [] } = item;
        return (
          <Row {...defaultRowAttribute} {...rowAttribute}>
            {children.map((item: FormItemPropsType) => createFormItem(item))}
          </Row>
        );
      })}
    </Space>
  </Form>
);

const formDom = ref(null);

onMounted(() => {
  emits('getFormRef', formDom.value);
});
</script>

<style lang="less" scoped></style>
